﻿
#region Copyright Info

// 
// Majesty of Omega - The next evolution of macro management space strategy
// 
// Copyright 2007-2009 André Claaßen
// 
// This file is protected by the terms and conditions of the
// Microsoft Community License (Ms-CL 1.1), a copy of which should
// have been distributed along with this software. If not,
// you may find the license information at the following URL:
// 
// http://www.microsoft.com/resources/sharedsource/licensingbasics/communitylicense.mspx

#endregion

#region

using System;
using System.Linq;
using Majesty_of_Omega.Config;
using DesignByContract;

#endregion

namespace Majesty_of_Omega.Model.Research
{
    /// <summary>
    /// An item of applied research. You can resarch choosen technologies in applied research. 
    /// To research you spread research points on your applied research.
    /// </summary>
    /// <remarks>
    /// Every applied research decreases the available research points. Many technologies of 
    /// applied research hinders the fundamental research.
    /// </remarks>
    [Serializable]
    public class AppliedResearchItem : ResearchItem
    {
        /// <summary>
        /// Researched of project
        /// </summary>
        private int percentageResearched;

        /// <summary>
        /// Researched percentage of this project
        /// </summary>
        public int PercentageResearched
        {
            get { return percentageResearched; }
            set { percentageResearched = value; }
        }

        /// <summary>
        /// Percentage of Research per turn
        /// </summary>
        private int percentageResearchPerTurn;

        /// <summary>
        /// Gets or sets the percentage research per turn.
        /// </summary>
        /// <value>The percentage research per turn.</value>
        public int PercentageResearchPerTurn
        {
            get { return percentageResearchPerTurn; }
            set
            {
                Require.IsTrue(value > 0 && value <= 100, "Value must be in percent and greater then 0");
                percentageResearchPerTurn = value;
            }
        }

        /// <summary>
        /// Percentage, when the project is fully researched (normally 100 Percent)
        /// </summary>
        private const int PercentageFullyResearched = 100;

        /// <summary>
        /// Initializes a new instance of the <see cref="AppliedResearchItem"/> class with the 
        /// given technology
        /// </summary>
        /// <param name="application">The application.</param>
        public AppliedResearchItem(TechApplication application)
            : base(application.Type_ID)
        {
            technology = application;
            BudgetFundingLevel = FundingLevel.Full;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AppliedResearchItem"/> class with the
        /// given technology
        /// </summary>
        /// <param name="techId">The tech_ ID.</param>
        public AppliedResearchItem(string techId)
            : base(techId)
        {
            BudgetFundingLevel = FundingLevel.Full;
        }

        /// <summary>
        /// Gets the expected number of remaining turns.
        /// </summary>
        /// <value>The expected number of remaining turns.</value>
        public int TurnsRemaining
        {
            get
            {
                Require.IsTrue(percentageResearchPerTurn > 0 );
                return (PercentageFullyResearched - percentageResearched) /percentageResearchPerTurn;
            }
        }

        /// <summary>
        /// Adds the research percentage for the current turn.
        /// </summary>
        public void AddResearchForTurn()
        {
            percentageResearched += percentageResearchPerTurn;
        }

        /// <summary>
        /// The current researched applied technology
        /// </summary>
        [NonSerialized] private TechApplication technology;

        /// <summary>
        /// Gets or sets the Technology.
        /// </summary>
        /// <remarks>
        /// Please use always this property, to get access to a technology
        /// </remarks>
        /// <value>The technology.</value>
        public TechApplication Technology
        {
            get
            {
                if (technology == null)
                {
                    technology = SpreadSheetManager.TechTables.TechAppDictionary[ResearchItemTypeID];
                }
                Ensure.IsNotNull(technology);
                return technology;
            }
        }

        /// <summary>
        /// Gets or sets the costs per turn
        /// </summary>
        /// <value>The turn cost.</value>
        public int TurnCost
        {
            get
            {
                Require.IsNotNull(Technology);
                Require.IsTrue(Technology.TechFieldDependencies.Count > 0);
                if (Technology.IsDefaultTechnology) return 0;

                // todo: the costs of the second field must also be calculated
                int costsPerLevel = GetCostsPerFieldDependency(Technology.TechFieldDependencies[0]);

                return costsPerLevel;
            }
        }

        /// <summary>
        /// Calculate the cost per field dependency
        /// </summary>
        /// <param name="fieldDependency">The field dependency.</param>
        /// <returns></returns>
        private static int GetCostsPerFieldDependency(TechFieldDependency fieldDependency)
        {
            var tfd = fieldDependency;
            return Convert.ToInt32((tfd.TechField.CalculateResearchPointsForLevel(tfd.TechLevel) -
                    tfd.TechField.CalculateResearchPointsForLevel(tfd.TechLevel - 1))*SpreadSheetManager.ResearchConfiguration.ResearchDefaults.PrimaryFieldCost);
        }

        /// <summary>
        /// Actual budget funding level
        /// </summary>
        private FundingLevel budgetFundingLevel;

        /// <summary>
        /// Gets the budget funding level info.
        /// </summary>
        /// <value>The budget funding level info.</value>
        public ResearchFundingLevelInfo BudgetFundingLevelInfo
        {
            get { return SpreadSheetManager.ResearchConfiguration.GetFundingLevelInfo(budgetFundingLevel); }
        }

        /// <summary>
        /// Gets or sets the budget <see cref="FundingLevel" />.
        /// </summary>
        /// <remarks>
        /// The to the funding level corrsponding research percentage will also be set
        /// </remarks>
        /// <value>The budget funding level.</value>
        public FundingLevel BudgetFundingLevel
        {
            get { return budgetFundingLevel; }
            set
            {
                budgetFundingLevel = value;
                ResearchPercentage = BudgetFundingLevelInfo.Percentage;
            }
        }

        /// <summary>
        /// Equalses the specified obj.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public bool Equals(AppliedResearchItem obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return base.Equals(obj) && Equals(obj.budgetFundingLevel, budgetFundingLevel);
        }

        /// <summary>
        /// Gets the progress in current level in percent.
        /// </summary>
        /// <value>The progress in current level.</value>
        /// <remarks>
        /// 70% means, that you must research 30% to enter the next level.
        /// </remarks>
        public override int ProgressInCurrentLevel
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Gets the icon of this research item.
        /// </summary>
        /// <value>The icon.</value>
        public override string Icon
        {
            get
            {
                return Technology.IconName;
            }
        }

        /// <summary>
        /// Gets the tech level for the primary technology field
        /// </summary>
        /// <value>The tech level.</value>
        public override int TechLevel
        {
            get
            {
                return Technology.TechFieldDependencies.First().TechLevel;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the current project is fully researched or not.
        /// </summary>
        /// <value><c>true</c> if [fully researched]; otherwise, <c>false</c>.</value>
        public bool IsFullyResearched
        {
            get
            {
                return percentageResearched >= PercentageFullyResearched;
            }
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return Equals(obj as AppliedResearchItem);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                {
                    return (base.GetHashCode()*397) ^ budgetFundingLevel.GetHashCode();
                }
            }
        }
    }
}